;-------------------------------------------------------------------------------
format ELF64 executable 3
entry start

include 'util/import64.inc'

interpreter '/lib64/ld-linux-x86-64.so.2'
needed 'libdl.so.2'
import dlopen,dlsym

segment readable executable

ENEIDA_UTIL_INCLUDE = 'CODE'
include 'util/util_amath.inc'
include 'util/util_vmath.inc'
include 'util/util_window.inc'
;-------------------------------------------------------------------------------
; name: init
;
align 16
init:
    sub rsp,8
    ; create vertex program
    call [glGenProgramsARB],<mov edi,1>,<mov rsi,g_vp>
    call [glNamedProgramStringEXT],\
        <mov edi,[g_vp]>,<mov esi,GL_VERTEX_PROGRAM_ARB>,<mov edx,GL_PROGRAM_FORMAT_ASCII_ARB>,<mov ecx,[g_src_vp_len]>,<mov r8,g_src_vp>
    ; create fragment program
    call [glGenProgramsARB],<mov edi,1>,<mov rsi,g_fp>
    call [glNamedProgramStringEXT],\
        <mov edi,[g_fp]>,<mov esi,GL_FRAGMENT_PROGRAM_ARB>,<mov edx,GL_PROGRAM_FORMAT_ASCII_ARB>,<mov ecx,[g_src_fp_len]>,<mov r8,g_src_fp>
    ; create vertex buffer
    call [glGenBuffers],<mov edi,1>,<mov rsi,g_vbo>
    call [glNamedBufferDataEXT],\
        <mov edi,[g_vbo]>,<mov esi,[g_vbo_size]>,<mov rdx,g_vbo_data>,<mov rcx,GL_STATIC_DRAW>
    ; get vbo address
    call [glGetNamedBufferParameterui64vNV],<mov edi,[g_vbo]>,<mov esi,GL_BUFFER_GPU_ADDRESS_NV>,<mov rdx,g_vbo_addr>
    call [glMakeBufferResidentNV],<mov edi,[g_vbo]>,<mov esi,GL_READ_ONLY>
    ; set GL state
    call [glEnable],<mov edi,GL_VERTEX_PROGRAM_ARB>
    call [glEnable],<mov edi,GL_FRAGMENT_PROGRAM_ARB>
    call [glEnable],<mov edi,GL_DEPTH_TEST>
    add rsp,8
    ret
;-------------------------------------------------------------------------------
; name: update
;
align 16
update:
    push rbp
    mov rbp,rsp
    sub rsp,128
    m equ rbp-64
    mvp equ rbp-128
    ; compute projection matrix
    emit vm_m4_perspective mvp,<vm_s_set xmm0,0.785398163>,<vm_s_set xmm1,1.6>,<vm_s_set xmm2,0.1>,<vm_s_set xmm3,10.0>
    ; compute view matrix
    emit vm_m4_lookat m,<vm_v3_set xmm0,2.0,2.0,2.0>,<vm_v3_set xmm1,0.0,0.0,0.0>,<vm_v3_set xmm2,0.0,1.0,0.0>
    vm_m4_mul_m4 mvp,mvp,m
    ; compute model matrix
    emit vm_m4_rotation_y m,<movss xmm0,[g_time]>
    vm_m4_mul_m4 mvp,mvp,m
    ; clear
    call [glClear],<mov edi,GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT>
    ; bind programs
    call [glBindProgramARB],<mov edi,GL_VERTEX_PROGRAM_ARB>,<mov esi,[g_vp]>
    call [glBindProgramARB],<mov edi,GL_FRAGMENT_PROGRAM_ARB>,<mov esi,[g_fp]>
    ; send matrix to the driver
    vm_m4_transpose mvp,mvp
    call [glNamedProgramLocalParameters4fvEXT],\
        <mov edi,[g_vp]>,<mov esi,GL_VERTEX_PROGRAM_ARB>,<mov edx,0>,<mov ecx,4>,<lea r8,[mvp]>
    ; enable vertex arrays
    call [glEnableClientState],<mov edi,GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV>
    call [glEnableVertexAttribArray],<mov edi,0>
    call [glEnableVertexAttribArray],<mov edi,1>
    ; set vertex array format
    call [glVertexAttribFormatNV],<mov edi,0>,<mov esi,2>,<mov edx,GL_FLOAT>,<mov ecx,GL_FALSE>,<mov r8d,20>
    call [glVertexAttribFormatNV],<mov edi,1>,<mov esi,3>,<mov edx,GL_FLOAT>,<mov ecx,GL_FALSE>,<mov r8d,20>
    ; set vertex array address
    mov rax,[g_vbo_addr]
    mov r8d,[g_vbo_size]
    call [glBufferAddressRangeNV],<mov edi,GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV>,<mov esi,0>,<lea rdx,[rax+0]>,<lea ecx,[r8d-0]>
    mov rax,[g_vbo_addr]
    mov r8d,[g_vbo_size]
    call [glBufferAddressRangeNV],<mov edi,GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV>,<mov esi,1>,<lea rdx,[rax+8]>,<lea ecx,[r8d-8]>
    ; draw
    call [glDrawArrays],<mov edi,GL_TRIANGLES>,<mov esi,0>,<mov edx,3>
    restore m,mvp
    mov rsp,rbp
    pop rbp
    ret
;-------------------------------------------------------------------------------
; name: quit
;
align 16
quit:
    sub rsp,8
    call window_close
    add rsp,8
    ret
;-------------------------------------------------------------------------------
; name: main
;
align 16
main:
    sub rsp,8
    call window_open,<mov edi,10>,<mov esi,10>,<mov edx,1440>,<mov ecx,900>
    ; init demo
    call init
    ; enter main loop
      .main_loop:
    ; get time
    call [clock],<mov edi,0>
    cvtsi2ss xmm0,eax
    mov eax,CLOCKS_PER_SEC
    cvtsi2ss xmm1,eax
    divss xmm0,xmm1
    movss [g_time],xmm0
    ; update demo
    call update
    ; swap buffers
    call window_swap
    test eax,eax
    jnz .main_loop_end
    jmp .main_loop
      .main_loop_end:
    add rsp,8
    ret
;-------------------------------------------------------------------------------
; name: start
;
align 16
start:
    call main
    ; terminate process
    call [exit],<mov edi,0>
;-------------------------------------------------------------------------------
segment readable writeable

ENEIDA_UTIL_INCLUDE = 'DATA'
include 'util/util_amath.inc'
include 'util/util_vmath.inc'
include 'util/util_window.inc'

align 8

g_vp dd 0
g_fp dd 0
g_vbo dd 0
g_vbo_size dd 60
g_vbo_addr dq 0
g_vbo_data dd -0.7,-0.7,1.0,0.0,0.0, 0.7,-0.7,0.0,1.0,0.0, 0.0,0.7,0.0,0.0,1.0
g_time dd 0

g_src_vp db '!!NVvp4.0'
NL 'FLOAT PARAM rot[4] = { program.local[0..3] };'
NL 'main:'
NL '    DP4.F result.position.x, rot[0], vertex.attrib[0];'
NL '    DP4.F result.position.y, rot[1], vertex.attrib[0];'
NL '    DP4.F result.position.z, rot[2], vertex.attrib[0];'
NL '    DP4.F result.position.w, rot[3], vertex.attrib[0];'
NL '    MOV.F result.attrib[0], vertex.attrib[1];'
NL '    RET;'
NL 'END',0
g_src_vp_len dd $-g_src_vp-1
g_src_fp db '!!NVfp4.0'
NL 'main:'
NL '    MOV.F result.color, fragment.attrib[0];'
NL '    RET;'
NL 'END',0
g_src_fp_len dd $-g_src_fp-1
;-------------------------------------------------------------------------------
